using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using DL_Content_Builder;
using System.Diagnostics;
using System.Net;
using System.IO;
using System.Xml;
using EsuApiLib;
using System.Configuration;


namespace MonsterMashGame2
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class ShowAtmosModel : Microsoft.Xna.Framework.GameComponent
    {

        // Cache information about the model size and position.
        Matrix[] boneTransforms;
        Vector3 modelCenter = new Vector3(10, 10, 10);
        float modelRadius = 2;
        Model starship;
        Model cylinder;
        // Timer controls the rotation speed.
        Stopwatch timer;
        protected Boolean screenReady = false;

        public ShowAtmosModel(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            if (screenReady)
            {
                return;
            }

            timer = new Stopwatch();
            timer.Start();
            getContentFromCloud();
            base.Initialize();
            screenReady = true;
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here

            base.Update(gameTime);
        }

        /// <summary>
        /// Whenever a new model is selected, we examine it to see how big
        /// it is and where it is centered. This lets us automatically zoom
        /// the display, so we can correctly handle models of any scale.
        /// </summary>
        void MeasureModel()
        {
            // Look up the absolute bone transforms for this model.
            boneTransforms = new Matrix[starship.Bones.Count];

            starship.CopyAbsoluteBoneTransformsTo(boneTransforms);

            // Compute an (approximate) model center position by
            // averaging the center of each mesh bounding sphere.
            modelCenter = Vector3.Zero;

            foreach (ModelMesh mesh in starship.Meshes)
            {
                BoundingSphere meshBounds = mesh.BoundingSphere;
                Matrix transform = boneTransforms[mesh.ParentBone.Index];
                Vector3 meshCenter = Vector3.Transform(meshBounds.Center, transform);

                modelCenter += meshCenter;
            }

            modelCenter /= starship.Meshes.Count;

            // Now we know the center point, we can compute the model radius
            // by examining the radius of each mesh bounding sphere.
            modelRadius = 0;

            foreach (ModelMesh mesh in starship.Meshes)
            {
                BoundingSphere meshBounds = mesh.BoundingSphere;
                Matrix transform = boneTransforms[mesh.ParentBone.Index];
                Vector3 meshCenter = Vector3.Transform(meshBounds.Center, transform);

                float transformScale = transform.Forward.Length();

                float meshRadius = (meshCenter - modelCenter).Length() +
                                   (meshBounds.Radius * transformScale);

                modelRadius = Math.Max(modelRadius, meshRadius);
            }
        }


        protected void getContentFromCloud()
        {
            #region Search online registry in cloud (Atmos service)

            //get information from Atmos service:
            String RegistryUsername = ConfigurationManager.AppSettings["RegistryUsername"];
            String RegistryPassword = ConfigurationManager.AppSettings["RegistryPassword"];
            String RegistryDB = ConfigurationManager.AppSettings["Registry"];
            XhiveServerSessionIfClient client = new XhiveServerSessionIfClient();
            String sessionID = client.connect(RegistryUsername, RegistryPassword, RegistryDB);

            String linksDoc = null;

            try
            {
                linksDoc = client.getDocument(sessionID, "/xbox1/links.xml", null, null);
            }
            catch (Exception ex1)
            {

            }

            if (String.IsNullOrEmpty(linksDoc))
            {
                return;
            }

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(linksDoc);

            XmlNodeList topNodes = doc.GetElementsByTagName("Links");
            XmlNode topNode = topNodes[0];

            XmlNodeList atmosNodes = doc.GetElementsByTagName("Atmos_Online");
            XmlNode atmosNode = atmosNodes[0];
            XmlNodeList atmosParams = atmosNode.ChildNodes;

            String hostUrl = null, portNum = null, tokenID = null, sharedSecret = null;

            foreach (XmlNode paramNode in atmosParams)
            {
                if (paramNode.Name.Equals("URL"))
                {
                    hostUrl = paramNode.InnerText;
                }
                else if (paramNode.Name.Equals("Port"))
                {
                    portNum = paramNode.InnerText;
                }
                else if (paramNode.Name.Equals("TokenID"))
                {
                    tokenID = paramNode.InnerText;
                }
                else if (paramNode.Name.Equals("SharedSecret"))
                {
                    sharedSecret = paramNode.InnerText;
                }
            }

            client.disconnect(sessionID);


            #endregion Search online registry in cloud (Atmos service)


            #region Download, build and run content

            int nPortNum = Int32.Parse(portNum);
            EsuApi atmosStorage = new EsuApiLib.Rest.EsuRestApi(hostUrl, nPortNum, tokenID, sharedSecret);
            ObjectPath objPath = new ObjectPath("/MonsterMash/" + CurrentState.AtmosModel);
            Byte[] buffer1 = new Byte[6000000];
            Byte[] objContents = atmosStorage.ReadObject(objPath, null, buffer1);

            String cylinderFilename = Game.Content.RootDirectory + "\\" + CurrentState.AtmosModel;
            FileStream cylinderContent = new FileStream(cylinderFilename, FileMode.Create);
            BinaryWriter cylinderWriter = new BinaryWriter(cylinderContent);
            cylinderWriter.Write(buffer1);
            cylinderWriter.Flush();
            cylinderWriter.Close();

            if (!String.IsNullOrEmpty(CurrentState.AtmosTexture1))
            {
                objPath = new ObjectPath("/MonsterMash/" + CurrentState.AtmosTexture1);
                Byte[] textureBuffer1 = new Byte[6000000];
                Byte[] textureContents1 = atmosStorage.ReadObject(objPath, null, textureBuffer1);
                String textureFilename1 = Game.Content.RootDirectory + "\\" + CurrentState.AtmosTexture1;
                FileStream textureContent1 = new FileStream(textureFilename1, FileMode.Create);
                BinaryWriter textureWriter1 = new BinaryWriter(textureContent1);
                textureWriter1.Write(textureBuffer1);
                textureWriter1.Flush();
                textureWriter1.Close();
            }

            if (!String.IsNullOrEmpty(CurrentState.AtmosTexture2))
            {
                objPath = new ObjectPath("/MonsterMash/" + CurrentState.AtmosTexture2);
                Byte[] textureBuffer2 = new Byte[6000000];
                Byte[] textureContents2 = atmosStorage.ReadObject(objPath, null, textureBuffer2);
                String textureFilename2 = Game.Content.RootDirectory + "\\" + CurrentState.AtmosTexture2;
                FileStream textureContent2 = new FileStream(textureFilename2, FileMode.Create);
                BinaryWriter textureWriter2 = new BinaryWriter(textureContent2);
                textureWriter2.Write(textureBuffer2);
                textureWriter2.Flush();
                textureWriter2.Close();
            }

            if (!String.IsNullOrEmpty(CurrentState.AtmosTexture3))
            {
                objPath = new ObjectPath("/MonsterMash/" + CurrentState.AtmosTexture3);
                Byte[] textureBuffer3 = new Byte[6000000];
                Byte[] textureContents3 = atmosStorage.ReadObject(objPath, null, textureBuffer3);
                String textureFilename3 = Game.Content.RootDirectory + "\\" + CurrentState.AtmosTexture3;
                FileStream textureContent3 = new FileStream(textureFilename3, FileMode.Create);
                BinaryWriter textureWriter3 = new BinaryWriter(textureContent3);
                textureWriter3.Write(textureBuffer3);
                textureWriter3.Flush();
                textureWriter3.Close();
            }

            ContentBuilder myBuilder = new ContentBuilder();
            myBuilder.Add(cylinderFilename, CurrentState.AtmosModel, null, "ModelProcessor");

            String buildError = myBuilder.Build();

            starship = Game.Content.Load<Model>(myBuilder.OutputDirectory + "\\" + CurrentState.AtmosModel);


            MeasureModel();


            #endregion Download, build and run content


        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Draw(GameTime gameTime)
        {
            Game.GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            // Compute camera matrices.
            float rotation = (float)timer.Elapsed.TotalSeconds;

            Vector3 eyePosition = modelCenter;

            eyePosition.Z += modelRadius * 2;
            eyePosition.Y += modelRadius;

            float aspectRatio = Game.GraphicsDevice.Viewport.AspectRatio;

            float nearClip = modelRadius / 100;
            float farClip = modelRadius * 100;

            Matrix world = Matrix.CreateRotationY(rotation);
            Matrix view = Matrix.CreateLookAt(eyePosition, modelCenter, Vector3.Up);
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(1, aspectRatio,
                                                                nearClip, farClip);

            // Draw the model.
            foreach (ModelMesh mesh in starship.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = boneTransforms[mesh.ParentBone.Index] * world;
                    effect.View = view;
                    effect.Projection = projection;

                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.SpecularPower = 16;
                }

                mesh.Draw();
            }

        }


    }
}